#include "cenario.h"

Cenario::Cenario()
{
    this->agente = NULL;
    this->objetivo = NULL;
    grid = (int**)malloc(sizeof(int) * TAMANHO_GRID);
    for(int i =0 ; i<TAMANHO_GRID; i++){
        grid[i] = (int*)malloc(sizeof(int) * TAMANHO_GRID);
    }
    this->zeraGrid();
}

int** Cenario::getGrid(){
    return this->grid;
}

int Cenario::getGridValue(int x,int y){
    return this->grid[x][y];
}

void Cenario::setGridValue( int x, int y , int valor){
    this->grid[x][y] = valor;
}

//detecta se ha colisao na determinada posicao
bool Cenario::detectaColisao(int x , int y, int tamanho){
    for(int i=-tamanho+1; i <=tamanho-1;i++){
        for(int j=-tamanho+1; j <=tamanho-1; j++){
            if( ( x+i>=0 ) && (x+i < TAMANHO_GRID) && ( y+j>=0 ) && (y+j < TAMANHO_GRID) ){
                if(this->grid[x+i][y+j] == OBSTACULO_GRID)return true;
            }
        }
    }
    return false;
}

//atualiza a matriz do grid com as distancias ao objetivo
void Cenario::atualizaGrid(){
    if(this->getObjetivo() == NULL) return;

    zeraGrid();

    int coordX = this->getObjetivo()->getx();
    int coordY = this->getObjetivo()->gety();
    this->grid[coordX][coordY] = 0;

    this->atualizaVizinhos(coordX,coordY,0);
}

//retira tudo do grid
void Cenario::zeraGrid(){
    for(int i = 0; i<=(TAMANHO_GRID-1) ;i++){
        for(int j = 0; j<=(TAMANHO_GRID-1) ; j++){
            if(this->grid[i][j] < OBSTACULO_GRID ) this->grid[i][j] = VAZIO_GRID;
        }
    }
}

//retira tudo do grid para o A*
void Cenario::zeraGridA(){
    for(int i = 0; i<=(TAMANHO_GRID-1) ;i++){
        for(int j = 0; j<=(TAMANHO_GRID-1) ; j++){
            if(this->grid[i][j] < OBSTACULO_GRID ) this->grid[i][j] = -1;
        }
    }
}

//atualiza a distancia ao objetivo dos vizinhos de determinada posicao do grid
void Cenario::atualizaVizinhos(int x,int y,int dist){
    if( (x > 0) && (y > 0) ){
        if( grid[x-1][y-1] > dist+1 && grid[x-1][y-1] < OBSTACULO_GRID){
            grid[x-1][y-1]= dist + 1;
            this->atualizaVizinhos(x-1,y-1,dist+1);
        }
    }
    if( x > 0){
        if( grid[x-1][y] > dist+1 && grid[x-1][y] < OBSTACULO_GRID){
            grid[x-1][y]= dist + 1;
            this->atualizaVizinhos(x-1,y,dist+1);
        }
    }
    if( (x > 0) && (y <= TAMANHO_GRID-2) ){
        if( grid[x-1][y+1] > dist+1 && grid[x-1][y+1] < OBSTACULO_GRID){
            grid[x-1][y+1]= dist + 1;
            this->atualizaVizinhos(x-1,y+1,dist+1);
        }
    }
    if( y <= TAMANHO_GRID-2){
        if( grid[x][y+1] > dist+1 && grid[x][y+1] < OBSTACULO_GRID){
            grid[x][y+1]= dist + 1;
            this->atualizaVizinhos(x,y+1,dist+1);
        }
    }
    if( (x <= TAMANHO_GRID-2) && (y <= TAMANHO_GRID-2) ){
        if( grid[x+1][y+1] > dist+1 && grid[x+1][y+1] < OBSTACULO_GRID){
            grid[x+1][y+1]= dist + 1;
            this->atualizaVizinhos(x+1,y+1,dist+1);
        }
    }
    if( x <= TAMANHO_GRID-2){
        if( grid[x+1][y] > dist+1 && grid[x+1][y] < OBSTACULO_GRID){
            grid[x+1][y]= dist + 1;
            this->atualizaVizinhos(x+1,y,dist+1);
        }
    }
    if( (x <= TAMANHO_GRID-2) && (y > 0) ){
        if( grid[x+1][y-1] > dist+1 && grid[x+1][y-1] < OBSTACULO_GRID){
            grid[x+1][y-1]= dist + 1;
            this->atualizaVizinhos(x+1,y-1,dist+1);
        }
    }
    if( y > 0){
        if( grid[x][y-1] > dist+1 && grid[x][y-1] < OBSTACULO_GRID){
            grid[x][y-1]= dist + 1;
            this->atualizaVizinhos(x,y-1,dist+1);
        }
    }
    return;
}


void Cenario::addAgente(Agente *agente){
    this->agente = agente;
}

void Cenario::addObjetivo(Objetivo *objetivo){    
    this->objetivo = objetivo;
    this->atualizaGrid();
}

void Cenario::addObstaculo(Obstaculo *obstaculo){
    this->listaDeObstaculos.push_back(obstaculo);
    this->atualizaObstaculoGrid(obstaculo->getx(),obstaculo->gety() );
}

void Cenario::atualizaObstaculoGrid( int x , int y){
    for(int i=-TAMANHO_OBSTACULO; i <=TAMANHO_OBSTACULO;i++){
        for(int j=-TAMANHO_OBSTACULO; j <=TAMANHO_OBSTACULO; j++){
            if( ( x+i>0 ) && (x+i < TAMANHO_GRID) && ( y+j>0 ) && (y+j < TAMANHO_GRID) ){
                this->grid[x+i][y+j] = OBSTACULO_GRID;
            }
        }
    }
}

void Cenario::addNPC(NonPlayableCharacter *NPC){
    this->listaDeNPCs.push_back(NPC);    
}

void Cenario::addWaypoint(Waypoint *waypoint){
    this->listaDeWaypoints.push_back(waypoint);
}

void Cenario::removeElemento(int posicaoX, int posicaoY){
    this->removeObstaculo(posicaoX,posicaoY);
    this->removeObjetivo(posicaoX,posicaoY);
    this->removeAgente(posicaoX,posicaoY);
    this->removeWaypoint(posicaoX,posicaoY);
    this->removeNPC(posicaoX,posicaoY);
}

void Cenario::removeAgente(int x, int y){
    if(this->agente == NULL)return;
    if( (x < this->getAgente()->getx()+this->getAgente()->getRaio()*50) && (x > this->getAgente()->getx()-this->getAgente()->getRaio()*50) ){
        if( (y < this->getAgente()->gety()+this->getAgente()->getRaio()*50) && (y > this->getAgente()->gety()-this->getAgente()->getRaio()*50) ){
            this->agente = NULL;
        }
    }
}

void Cenario::removeObjetivo(int x, int y){
    if(this->objetivo == NULL)return;
    if( (x < this->getObjetivo()->getx() + this->getObjetivo()->getRaio()*50) && (x > this->getObjetivo()->getx()-this->getObjetivo()->getRaio()*50) ){
        if( (y < this->getObjetivo()->gety()+this->getObjetivo()->getRaio()*50) && (y > this->getObjetivo()->gety()-this->getObjetivo()->getRaio()*50) ){
            this->objetivo = NULL;
            zeraGrid();
        }
    }

}

void Cenario::removeObstaculo(int x,int y){
    for(int i = 0; i < this->getListadeObstaculosSize(); i++){        
        if( (x <= this->getObstaculo(i)->getx()+TAMANHO_OBSTACULO) && (x >= this->getObstaculo(i)->getx() - TAMANHO_OBSTACULO )){
            if( (y <= this->getObstaculo(i)->gety()+TAMANHO_OBSTACULO) && (y >= this->getObstaculo(i)->gety()- TAMANHO_OBSTACULO)){

                for(int k=-TAMANHO_OBSTACULO; k <=TAMANHO_OBSTACULO;k++){
                    for(int j=-TAMANHO_OBSTACULO; j <=TAMANHO_OBSTACULO; j++){
                        if( ( this->getObstaculo(i)->getx()+k>0 ) && (this->getObstaculo(i)->getx()+k < TAMANHO_GRID) && ( this->getObstaculo(i)->gety()+j>0 ) && (this->getObstaculo(i)->gety()+j < TAMANHO_GRID) ){
                            this->grid[x+k][y+j] = VAZIO_GRID;
                        }
                    }
                }

                this->listaDeObstaculos.erase(listaDeObstaculos.begin()+i );
                zeraGrid();
                atualizaGrid();
                return ;                
            }
        }
    }
}

void Cenario::removeNPC(int x,int y){

    for(int i = 0; i < this->getListadeNPCsSize(); i++){
        if( (x < this->getNPC(i)->getx()+this->getNPC(i)->getRaio()*50) && (x > this->getNPC(i)->getx()-this->getNPC(i)->getRaio()*50) ){
            if( (y < this->getNPC(i)->gety()+this->getNPC(i)->getRaio()*50) && (y > this->getNPC(i)->gety()-this->getNPC(i)->getRaio()*50) ){
                this->listaDeNPCs.erase(listaDeNPCs.begin()+i );
            }
        }
    }
}

void Cenario::removeWaypoint(int x, int y){

    for(int i = 0; i < this->getListadeWaypointsSize(); i++){
        if( (x < this->getWaypoint(i)->getx()+this->getWaypoint(i)->getRaio()*50) && (x > this->getWaypoint(i)->getx()-this->getWaypoint(i)->getRaio()*50) ){
            if( (y < this->getWaypoint(i)->gety()+this->getWaypoint(i)->getRaio()*50) && (y > this->getWaypoint(i)->gety()-this->getWaypoint(i)->getRaio()*50) ){
                this->listaDeWaypoints.erase(listaDeWaypoints.begin()+i );
            }
        }
    }
}

void Cenario::limpaCenario(){
    this->agente = NULL;
    this->objetivo = NULL;
    listaDeObstaculos.clear();
    listaDeNPCs.clear();
    listaDeWaypoints.clear();
    zeraGrid();
    atualizaGrid();
}

Agente* Cenario::getAgente(){
    return this->agente;
}
Objetivo* Cenario::getObjetivo(){
    return this->objetivo;
}

Obstaculo* Cenario::getObstaculo(int indice){
    return this->listaDeObstaculos.at(indice);
}

NonPlayableCharacter* Cenario::getNPC(int indice){
    return this->listaDeNPCs.at(indice);
}

Waypoint* Cenario::getWaypoint(int indice){
    return this->listaDeWaypoints.at(indice);
}

int Cenario::getListadeObstaculosSize(){
    return this->listaDeObstaculos.size();
}

int Cenario::getListadeNPCsSize(){
    return this->listaDeNPCs.size();
}

int Cenario::getListadeWaypointsSize(){
    return this->listaDeWaypoints.size();
}
